package org.example.hrpc.config.reference;

import org.example.hrpc.common.URL;
import org.example.hrpc.common.generic.GenericService;
import org.example.hrpc.common.spi.loader.ExtensionLoader;
import org.example.hrpc.common.utils.CollectionUtils;
import org.example.hrpc.common.utils.StringUtils;
import org.example.hrpc.config.RegistryConfig;
import org.example.hrpc.rpc.core.Invoker;
import org.example.hrpc.rpc.core.Protocol;
import org.example.hrpc.rpc.core.ProxyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

import static org.example.hrpc.common.constant.CommonConstant.GENERIC_SERIALIZATION_DEFAULT;

public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
    public static final Logger logger = LoggerFactory.getLogger(ReferenceConfig.class);

    private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("registry");
    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("jdk");

    /**
     * The interface proxy reference
     */
    private transient volatile T ref;
    /**
     * The invoker of the reference service
     */
    private transient volatile Invoker<?> invoker;

    /**
     * The flag whether the ReferenceConfig has been initialized
     */
    private transient boolean initialized;

    /**
     * whether this ReferenceConfig has been destroyed
     */
    private transient volatile boolean destroyed;

    /**
     * The url of the reference service
     */
    protected final List<URL> urls = new ArrayList<URL>();

    /**
     * The registry list the service will register to
     */
    protected List<RegistryConfig> registries;



    public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
        }
        if (ref == null) {
            init();
        }
        return ref;
    }

    public synchronized void destroy() {
        if (ref == null) {
            return;
        }
        if (destroyed) {
            return;
        }
        destroyed = true;
        try {
//            invoker.destroy();
        } catch (Throwable t) {
            logger.warn("Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t);
        }
        invoker = null;
        ref = null;
    }

    public void init() {
        if(initialized) {
            return;
        }
        // 检测接口名合法性

        // 检测是否是泛化接口
        if(isGeneric(generic)) {
            interfaceClass = GenericService.class;
        } else {
            // 非泛化调用
            try {
                interfaceClass = Class.forName(interfaceName, true,
                        Thread.currentThread().getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        // 创建URL，填充好
//        URL url = new URL("hrpc", "127.0.0.1", 30000, "org.example.hrpc.example.HelloService", null);
        URL url = new URL(this.protocolName, this.bindToHost, this.bindToPort, this.interfaceName, null);
        urls.add(url);
        // 创建代理类
        ref = createProxy(url);

        initialized = true;
    }

    // 返回是否是泛化接口
    private boolean isGeneric(String generic) {
        return StringUtils.isNotEmpty(generic)
                && (
                    GENERIC_SERIALIZATION_DEFAULT.equalsIgnoreCase(generic)
                );
    }


    /**
     * 该类创建代理对象，
     * @return
     */
    private T createProxy(URL url) {
        // 调用 RegistryProtocol 的 refer 构建 Invoker 实例
        // 目前仅实现一个注册中心
        loadRegistries();
        // 服务直连方式 或者 仅有一个注册中心
        if(urls.size() == 1){
            // Invoker是由Protocol的实现类构建而来
            invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
        } else {
            logger.warn("hrpc will support multi registry in the future");
        }

        // 生成代理类
        return (T) proxyFactory.getProxy(invoker);
    }

    private void loadRegistries() {
        if(CollectionUtils.isNotEmpty(registries)) {
            for(RegistryConfig config : registries) {
                for(URL url : urls) {
                    url.setProtocol(config.getProtocol());
                    url.setHost(config.getAddress());
                    url.setPort(config.getPort());
                    url.setParameters(new String[]{"register=true"});
                }
            }
        }
    }

    public void addRegistry(RegistryConfig registryConfig) {
        if(registries == null) {
            registries = new ArrayList<>();
        }
        registries.add(registryConfig);
    }
}
